/*
 * Copyright (c) 2023-2025 elsfs Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.elsfs.cloud.common.util.lang;

/** CharSequenceUtils类提供了一系列用于处理CharSequence对象的工具方法。 该类包含各种字符串操作的实用方法，如比较、查找、截取等操作。 */
public class CharSequenceUtils {
  /** 字符串常量：空字符串 {@code ""} */
  public static final String EMPTY = "";

  // ------------------------------------------------------------------------ strip
  /**
   * 去除两边的指定字符串
   *
   * @param str 被处理的字符串
   * @param prefixOrSuffix 前缀或后缀
   * @return 处理后的字符串
   * @since 3.1.2
   */
  public static String strip(CharSequence str, CharSequence prefixOrSuffix) {
    if (equals(str, prefixOrSuffix)) {
      // 对于去除相同字符的情况单独处理
      return EMPTY;
    }
    return strip(str, prefixOrSuffix, prefixOrSuffix);
  }

  /**
   * 去除两边的指定字符串
   *
   * @param str 被处理的字符串
   * @param prefix 前缀
   * @param suffix 后缀
   * @return 处理后的字符串
   * @since 3.1.2
   */
  public static String strip(CharSequence str, CharSequence prefix, CharSequence suffix) {
    if (isEmpty(str)) {
      return str(str);
    }

    int from = 0;
    int to = str.length();

    String str2 = str.toString();
    if (startWith(str2, prefix)) {
      from = prefix.length();
    }
    if (endWith(str2, suffix)) {
      to -= suffix.length();
    }

    return str2.substring(Math.min(from, to), Math.max(from, to));
  }

  /**
   * 是否以指定字符串结尾
   *
   * @param str 被监测字符串
   * @param suffix 结尾字符串
   * @return 是否以指定字符串结尾
   */
  public static boolean endWith(CharSequence str, CharSequence suffix) {
    return endWith(str, suffix, false);
  }

  /**
   * 是否以指定字符串结尾<br>
   * 如果给定的字符串和开头字符串都为null则返回true，否则任意一个值为null返回false
   *
   * @param str 被监测字符串
   * @param suffix 结尾字符串
   * @param ignoreCase 是否忽略大小写
   * @return 是否以指定字符串结尾
   */
  public static boolean endWith(CharSequence str, CharSequence suffix, boolean ignoreCase) {
    return endWith(str, suffix, ignoreCase, false);
  }

  /**
   * 是否以指定字符串结尾<br>
   * 如果给定的字符串和开头字符串都为null则返回true，否则任意一个值为null返回false
   *
   * @param str 被监测字符串
   * @param suffix 结尾字符串
   * @param ignoreCase 是否忽略大小写
   * @param ignoreEquals 是否忽略字符串相等的情况
   * @return 是否以指定字符串结尾
   * @since 5.8.0
   */
  public static boolean endWith(
      CharSequence str, CharSequence suffix, boolean ignoreCase, boolean ignoreEquals) {
    if (null == str || null == suffix) {
      if (ignoreEquals) {
        return false;
      }
      return null == str && null == suffix;
    }

    final int strOffset = str.length() - suffix.length();
    boolean isEndWith =
        str.toString().regionMatches(ignoreCase, strOffset, suffix.toString(), 0, suffix.length());

    if (isEndWith) {
      return (!ignoreEquals) || (!equals(str, suffix, ignoreCase));
    }
    return false;
  }

  /**
   * 字符串是否以给定字符结尾
   *
   * @param str 字符串
   * @param c 字符
   * @return 是否结尾
   */
  public static boolean endWith(CharSequence str, char c) {
    if (isEmpty(str)) {
      return false;
    }
    return c == str.charAt(str.length() - 1);
  }

  /**
   * 是否以指定字符串开头
   *
   * @param str 被监测字符串
   * @param prefix 开头字符串
   * @return 是否以指定字符串开头
   */
  public static boolean startWith(CharSequence str, CharSequence prefix) {
    return startWith(str, prefix, false);
  }

  /**
   * 字符串是否以给定字符开始
   *
   * @param str 字符串
   * @param c 字符
   * @return 是否开始
   */
  public static boolean startWith(CharSequence str, char c) {
    if (isEmpty(str)) {
      return false;
    }
    return c == str.charAt(0);
  }

  /**
   * 是否以指定字符串开头<br>
   * 如果给定的字符串和开头字符串都为null则返回true，否则任意一个值为null返回false
   *
   * @param str 被监测字符串
   * @param prefix 开头字符串
   * @param ignoreCase 是否忽略大小写
   * @return 是否以指定字符串开头
   * @since 5.4.3
   */
  public static boolean startWith(CharSequence str, CharSequence prefix, boolean ignoreCase) {
    return startWith(str, prefix, ignoreCase, false);
  }

  /**
   * 是否以指定字符串开头<br>
   * 如果给定的字符串和开头字符串都为null则返回true，否则任意一个值为null返回false<br>
   *
   * <pre>
   *     CharSequenceUtil.startWith("123", "123", false, true);   -- false
   *     CharSequenceUtil.startWith("ABCDEF", "abc", true, true); -- true
   *     CharSequenceUtil.startWith("abc", "abc", true, true);    -- false
   * </pre>
   *
   * @param str 被监测字符串
   * @param prefix 开头字符串
   * @param ignoreCase 是否忽略大小写
   * @param ignoreEquals 是否忽略字符串相等的情况
   * @return 是否以指定字符串开头
   * @since 5.4.3
   */
  public static boolean startWith(
      CharSequence str, CharSequence prefix, boolean ignoreCase, boolean ignoreEquals) {
    if (null == str || null == prefix) {
      if (ignoreEquals) {
        return false;
      }
      return null == str && null == prefix;
    }

    boolean isStartWith =
        str.toString().regionMatches(ignoreCase, 0, prefix.toString(), 0, prefix.length());

    if (isStartWith) {
      return (!ignoreEquals) || (!equals(str, prefix, ignoreCase));
    }
    return false;
  }

  /**
   * {@link CharSequence} 转为字符串，null安全
   *
   * @param cs {@link CharSequence}
   * @return 字符串
   */
  public static String str(CharSequence cs) {
    return null == cs ? null : cs.toString();
  }

  /**
   * 字符串是否为空，空的定义如下：
   *
   * <ol>
   *   <li>{@code null}
   *   <li>空字符串：{@code ""}
   * </ol>
   *
   * <p>例：
   *
   * <ul>
   *   <li>{@code StrUtil.isEmpty(null) // true}
   *   <li>{@code StrUtil.isEmpty("") // true}
   *   <li>{@code StrUtil.isEmpty(" \t\n") // false}
   *   <li>{@code StrUtil.isEmpty("abc") // false}
   * </ul>
   *
   * <p>注意：该方法与 {@link #isBlank(CharSequence)} 的区别是：该方法不校验空白字符。
   *
   * <p>建议：
   *
   * <ul>
   *   <li>该方法建议用于工具类或任何可以预期的方法参数的校验中。
   *   <li>需要同时校验多个字符串时，建议采用 {@link #hasEmpty(CharSequence...)} 或 {@link
   *       #isAllEmpty(CharSequence...)}
   * </ul>
   *
   * @param str 被检测的字符串
   * @return 是否为空
   * @see #isBlank(CharSequence)
   */
  public static boolean isEmpty(CharSequence str) {
    return str == null || str.isEmpty();
  }

  // ------------------------------------------------------------------------ equals

  /**
   * 比较两个字符串（大小写敏感）。
   *
   * <pre>
   * equals(null, null)   = true
   * equals(null, &quot;abc&quot;)  = false
   * equals(&quot;abc&quot;, null)  = false
   * equals(&quot;abc&quot;, &quot;abc&quot;) = true
   * equals(&quot;abc&quot;, &quot;ABC&quot;) = false
   * </pre>
   *
   * @param str1 要比较的字符串1
   * @param str2 要比较的字符串2
   * @return 如果两个字符串相同，或者都是{@code null}，则返回{@code true}
   */
  public static boolean equals(CharSequence str1, CharSequence str2) {
    return equals(str1, str2, false);
  }

  /**
   * 比较两个字符串是否相等，规则如下
   *
   * <ul>
   *   <li>str1和str2都为{@code null}
   *   <li>忽略大小写使用{@link String#equalsIgnoreCase(String)}判断相等
   *   <li>不忽略大小写使用{@link String#contentEquals(CharSequence)}判断相等
   * </ul>
   *
   * @param str1 要比较的字符串1
   * @param str2 要比较的字符串2
   * @param ignoreCase 是否忽略大小写
   * @return 如果两个字符串相同，或者都是{@code null}，则返回{@code true}
   * @since 3.2.0
   */
  public static boolean equals(CharSequence str1, CharSequence str2, boolean ignoreCase) {
    if (null == str1) {
      // 只有两个都为null才判断相等
      return str2 == null;
    }
    if (null == str2) {
      // 字符串2空，字符串1非空，直接false
      return false;
    }

    if (ignoreCase) {
      return str1.toString().equalsIgnoreCase(str2.toString());
    } else {
      return str1.toString().contentEquals(str2);
    }
  }

  /**
   * 比较两个字符串（大小写不敏感）。
   *
   * <pre>
   * equalsIgnoreCase(null, null)   = true
   * equalsIgnoreCase(null, &quot;abc&quot;)  = false
   * equalsIgnoreCase(&quot;abc&quot;, null)  = false
   * equalsIgnoreCase(&quot;abc&quot;, &quot;abc&quot;) = true
   * equalsIgnoreCase(&quot;abc&quot;, &quot;ABC&quot;) = true
   * </pre>
   *
   * @param str1 要比较的字符串1
   * @param str2 要比较的字符串2
   * @return 如果两个字符串相同，或者都是{@code null}，则返回{@code true}
   */
  public static boolean equalsIgnoreCase(CharSequence str1, CharSequence str2) {
    return equals(str1, str2, true);
  }
}
